/**
 * 维护正在进行的聊天会话的状态
 */
import { truthy, getLogger, Service } from '@gnlab/utils'
import { MemSubject } from '@gnlab/utils/rx'
import type { Services } from '@/services'
import type { Contact } from '@/services/contacts'

const logger = getLogger('services/chat/in-progress')

export interface InProgressInfo {
  initialUnread: number // 进入会话时有几条未读消息

  focusMsgId: string | null // 是否指定要查看某条聊天记录。
  // 在联系人刚成为联系人时，以及成为联系人之后，都可能被赋值（例如通过聊天记录搜索界面触发要跳到某条聊天记录）。
  // 使用者需要注意监听此值的变化。

  // 有值代表与此联系人的聊天处于 "pending" 状态，即用户在查看前面的聊天记录，无法立刻看到最新的消息。
  // 其中，为 true 代表处于 pending 状态但还没有收到新消息；为 string 代表已经收到了新消息，此为第一条新消息的 id。
  pending: null | true | string
}

export class InProgress extends Service<Services> {
  private get contacts() {
    return this.services.contacts
  }
  private get mutes() {
    return this.services.groups.mutes
  }
  private get currentId() {
    return this.contacts.current.id
  }
  private get chat() {
    return this.services.chat
  }
  private get metas() {
    return this.chat.metas
  }

  // 当前会话状态，没有时为 null
  private readonly _info = new MemSubject<InProgressInfo | null>(null)
  readonly info = this._info.asStream()

  registered() {
    this.initResetInfo()
    this.initRecordPending()
  }

  // =============================================

  /**
   * 在每次切换联系人时初始化 / 重置记录的信息
   */
  private memoryFocusMsgId: { contactId: string; msgId: string } | null = null // 切换联系人前先给此属性赋值，可指定此联系人初始要跳转的聊天记录
  private initResetInfo() {
    this.currentId.subscribe(id => {
      const focusMsgId =
        this.memoryFocusMsgId && this.memoryFocusMsgId.contactId === id
          ? this.memoryFocusMsgId.msgId
          : null
      this.memoryFocusMsgId = null

      if (truthy(id)) {
        // 对于“免打扰”的群，不考虑未读消息数量，总是认为没有未读消息
        // （例如进入聊天时，会直接进入最下面）
        const isMutedGroup = this.mutes.value.has(id)
        const unread = isMutedGroup ? 0 : this.metas.unreads.value.get(id)?.unread ?? 0
        this._info.next({
          initialUnread: unread,
          focusMsgId,
          pending: null,
        })
      } else {
        this._info.next(null)
      }
    })
  }

  /**
   * 切换到与指定联系人的会话，并指定要查看的聊天记录
   */
  markWithFocus(contactId: string | Contact, msgId: string) {
    // 如果联系人不存在，不触发 memory 设置和 current.mark()。
    // 不然会导致 memory 残留（因为 current.mark() 碰到联系人不存在的情况就不会更新 current.id，也就不会触发 initAutoResetInfo() 里的更新行为）
    const contact = typeof contactId === 'string' ? this.contacts.getById(contactId) : contactId
    if (!contact)
      return void logger.warn(`找不到 id=${contactId as string} 的联系人，无法将其设为当前联系人`)

    // 如果目标联系人已经是“当前联系人”，直接更新 focusMsgId
    if (contact.id === this.contacts.current.id.value && this.info.value) {
      this._info.next({
        ...this.info.value,
        focusMsgId: msgId,
      })
    } else {
      // 记录目标聊天记录ID，并切换联系人
      this.memoryFocusMsgId = {
        contactId: typeof contactId === 'string' ? contactId : contactId.id,
        msgId,
      }
      this.contacts.current.mark(contactId)
    }
  }

  // =============================================

  /**
   * 记录 pending 状态下的新消息数据
   */
  private initRecordPending() {
    this.chat.received.subscribe(items => {
      Object.entries(items).forEach(([contactId, { records }]) => {
        if (contactId === this.currentId.value && this.info.value?.pending === true) {
          this._info.next({ ...this.info.value, pending: records[records.length - 1]!.id })
        }
      })
    })
  }

  /**
   * 标记当前会话是否处于 pending 状态
   */
  markPending(isPending: boolean) {
    const info = this.info.value
    if (!info) return void logger.warn('没有当前会话，无法更新 pending 状态')

    if (isPending && !truthy(info.pending)) {
      this._info.next({ ...info, pending: true })
    } else if (!isPending && truthy(info.pending)) {
      this._info.next({ ...info, pending: null })
    }
  }

  /**
   * 重置 pending 状态（即仍处于 pending 状态，但是重新统计第一条未读消息的 id）
   */
  resetPending() {
    const info = this.info.value
    if (typeof info?.pending === 'string') {
      this._info.next({ ...info, pending: true })
    }
  }
}
